package com.atguigu.daijia.map.service.impl;

import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.constant.SystemConstant;
import com.atguigu.daijia.common.result.Result;
import com.atguigu.daijia.common.util.LocationUtil;
import com.atguigu.daijia.driver.client.DriverInfoFeignClient;
import com.atguigu.daijia.map.repository.OrderServiceLocationRepository;
import com.atguigu.daijia.map.service.LocationService;
import com.atguigu.daijia.model.entity.driver.DriverSet;
import com.atguigu.daijia.model.entity.map.OrderServiceLocation;
import com.atguigu.daijia.model.form.map.OrderServiceLocationForm;
import com.atguigu.daijia.model.form.map.SearchNearByDriverForm;
import com.atguigu.daijia.model.form.map.UpdateDriverLocationForm;
import com.atguigu.daijia.model.form.map.UpdateOrderLocationForm;
import com.atguigu.daijia.model.vo.driver.DriverInfoVo;
import com.atguigu.daijia.model.vo.map.NearByDriverVo;
import com.atguigu.daijia.model.vo.map.OrderLocationVo;
import com.atguigu.daijia.model.vo.map.OrderServiceLastLocationVo;
import com.atguigu.daijia.order.client.OrderInfoFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.*;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.awt.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class LocationServiceImpl implements LocationService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DriverInfoFeignClient driverClient;
    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;
    @Autowired
    private OrderServiceLocationRepository locationRepository;
    @Autowired
    private MongoTemplate mongoTemplate;


    //司机开始接单，更新司机的位置信息(上传位置到rerdis的GEO)
    @Override
    public Boolean updateDriverLocation(UpdateDriverLocationForm locationForm) {
        /**
         *  Redis GEO 主要用于存储地理位置信息，并对存储的信息进行相关操作，该功能在 Redis 3.2 版本新增。
         *  后续用在，乘客下单后寻找5公里范围内开启接单服务的司机，通过Redis GEO进行计算
         */
        Point point = new Point(locationForm.getLongitude().doubleValue(), locationForm.getLatitude().doubleValue());
        //key+位置(经度、纬度)+司机ID geoadd key 10 15 diverId
        redisTemplate.opsForGeo().add(RedisConstant.DRIVER_GEO_LOCATION, point, locationForm.getDriverId().toString());
        return true;
    }

    //司机下线，移除司机的位置信息
    @Override
    public Boolean removeDriverLocation(Long driverId) {
        redisTemplate.opsForGeo().remove(RedisConstant.DRIVER_GEO_LOCATION, driverId.toString());
        return true;
    }

    //搜索五公里以内满足条件的司机
    @Override
    public List<NearByDriverVo> searchNearByDriver(SearchNearByDriverForm nearByDriverForm) {
        //定义中心点
        Point point = new Point(nearByDriverForm.getLongitude().doubleValue(), nearByDriverForm.getLatitude().doubleValue());
        //定义距离：5公里(系统配置)
        Distance distance = new Distance(SystemConstant.NEARBY_DRIVER_RADIUS, RedisGeoCommands.DistanceUnit.KILOMETERS);
        //定义以point点为中心，distance为距离这么一个范围
        Circle circle = new Circle(point, distance);

        //定义GEO参数
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                .includeDistance() //包含距离
                .includeCoordinates() //包含坐标
                .sortAscending(); //排序：升序

        // 1.GEORADIUS获取附近范围内的信息
        GeoResults<RedisGeoCommands.GeoLocation<String>> result = this.redisTemplate.opsForGeo().radius(RedisConstant.DRIVER_GEO_LOCATION, circle, args);

        //2.收集信息，存入list
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = result.getContent();

        //3.返回计算后的信息
        List<NearByDriverVo> list = new ArrayList();
        if (!CollectionUtils.isEmpty(content)) {
            Iterator<GeoResult<RedisGeoCommands.GeoLocation<String>>> iterator = content.iterator();
            while (iterator.hasNext()) {
                GeoResult<RedisGeoCommands.GeoLocation<String>> item = iterator.next();
                //司机ID
                Long diverId = Long.parseLong(item.getContent().getName());
                //司机ID远程调用获取司机的个性设置
                Result<DriverSet> diverSet = driverClient.getDriverSet(diverId);
                BigDecimal orderDistance = diverSet.getData().getOrderDistance();

                //司机接受的距离小于订单的距离，不满足
                if (orderDistance.doubleValue() != 0 &&
                        orderDistance.doubleValue() < nearByDriverForm.getMileageDistance().doubleValue())
                    continue;
                //订单距离司机的可接受距离
                BigDecimal acceptDistance = diverSet.getData().getAcceptDistance();
                //订单距离司机的实际距离
                BigDecimal shijiDistance = new BigDecimal(item.getDistance().getValue()).setScale(2, RoundingMode.HALF_UP);
                if (acceptDistance.doubleValue() != 0 && acceptDistance.doubleValue() < shijiDistance.doubleValue())
                    continue;
                //封装符合条件的司机
                NearByDriverVo nearByDriverVo = new NearByDriverVo();
                nearByDriverVo.setDriverId(diverId);
                nearByDriverVo.setDistance(shijiDistance);
                list.add(nearByDriverVo);

            }
        }
        return list;
    }

    //司机赶往代驾起始点：更新司机位置到缓存
    @Override
    public Boolean updateOrderLocationToCache(UpdateOrderLocationForm locationForm) {
        String key = "update:order:location:" + locationForm.getOrderId();
        OrderLocationVo orderLocationVo = new OrderLocationVo();
        orderLocationVo.setLatitude(locationForm.getLatitude());
        orderLocationVo.setLongitude(locationForm.getLongitude());
        redisTemplate.opsForValue().set(key, orderLocationVo);
        return true;
    }

    //司机赶往代驾起始点：获取订单(司机)经纬度位置
    @Override
    public OrderLocationVo getCacheOrderLocation(Long orderId) {
        String key = "update:order:location:" + orderId;
        OrderLocationVo orderLocationVo = (OrderLocationVo) redisTemplate.opsForValue().get(key);
        return orderLocationVo;
    }

    //开始代驾服务：批量保存代驾服务订单位置
    @Override
    public Boolean saveOrderServiceLocation(List<OrderServiceLocationForm> orderLocationServiceFormList) {
        //批量插入的集合
        ArrayList<OrderServiceLocation> list = new ArrayList<>();
        orderLocationServiceFormList.forEach(locationForm -> {
            OrderServiceLocation orderServiceLocation = new OrderServiceLocation();
            BeanUtils.copyProperties(locationForm, orderServiceLocation);
            orderServiceLocation.setId(ObjectId.get().toString());//设置Id
            orderServiceLocation.setCreateTime(new Date());
            list.add(orderServiceLocation);
        });
        //添加到MongoDB
        locationRepository.saveAll(list);
        return true;
    }

    //代驾服务：获取订单服务最后一个位置信息
    @Override
    public OrderServiceLastLocationVo getOrderServiceLastLocation(Long orderId) {
        Query query = new Query();
        //根据orderID查询
        query.addCriteria(Criteria.where("orderId").is(orderId));
        //时间降序排列
        query.with(Sort.by(Sort.Order.desc("createTime")));
        //获取第一条
        query.limit(1);
        OrderServiceLocation orderServiceLocation = mongoTemplate.findOne(query, OrderServiceLocation.class);
        //封装返回数据
        OrderServiceLastLocationVo vo = new OrderServiceLastLocationVo();
        BeanUtils.copyProperties(orderServiceLocation, vo);
        return vo;
    }

    //代驾服务：计算订单实际里程
    @Override
    public BigDecimal calculateOrderRealDistance(Long orderId) {

        List<OrderServiceLocation> serviceLocations = locationRepository.findByOrderIdOrderByCreateTimeAsc(orderId);
        double realdistance = 0;
        if (!CollectionUtils.isEmpty(serviceLocations)) {
            for (int i = 0; i < serviceLocations.size() - 1; i++) {
                //起始位置
                OrderServiceLocation location1 = serviceLocations.get(i);
                //结束位置
                OrderServiceLocation location2 = serviceLocations.get(i + 1);
                double distance = LocationUtil.getDistance(
                        location1.getLatitude().doubleValue(),
                        location1.getLongitude().doubleValue(),
                        location2.getLatitude().doubleValue(),
                        location2.getLongitude().doubleValue());
                realdistance += distance;
            }
        }
        //
        // TODO 模拟测试：实际代驾里程 = 预期里程 + 5
        if(realdistance == 0) {
            return orderInfoFeignClient.getOrderInfo(orderId).getData().getExpectDistance().add(new BigDecimal("5"));
        }

        //返回最终的实际距离
        return new BigDecimal(realdistance);
    }

}
